1 /* 2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021 3 License: [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License]. 4 Authors: Marcelo S. N. Mancini 5 6 Copyright Marcelo S. N. Mancini 2018 - 2021. 7 Distributed under the CC BY-4.0 License. 8 (See accompanying file LICENSE.txt or copy at 9 https://creativecommons.org/licenses/by/4.0/ 10 */ 11 module hip.util.reflection; 12 13 /** 14 * Used when wanting to represent any struct compatible with a static array. 15 */ 16 bool isTypeArrayOf(Type, Array, int Count)() 17 { 18 static if(is(Array == Type[Count])) 19 return true; 20 else static if(!is(Array == struct)) 21 return false; 22 else 23 { 24 int count = 0; 25 static foreach(v; Array.tupleof) 26 static if(is(typeof(v) == Type)) 27 count++; 28 else static if(__traits(isStaticArray, typeof(v)) && is(typeof(v.init[0]) == Type)) 29 count+= v.length; 30 return count == Count; 31 } 32 } 33 34 template isDynamicArray(T) 35 { 36 static if (is(T == U[], U)) 37 enum bool isDynamicArray = true; 38 else static if (is(T U == enum)) 39 // BUG: isDynamicArray / isStaticArray considers enums 40 // with appropriate base types as dynamic/static arrays 41 // Retain old behaviour for now, see 42 // https://github.com/dlang/phobos/pull/7574 43 enum bool isDynamicArray = isDynamicArray!U; 44 else 45 enum bool isDynamicArray = false; 46 } 47 48 enum bool isArray(T) = isDynamicArray!T || __traits(isStaticArray, T); 49 50 /** 51 * Detect whether type `T` is a pointer. 52 */ 53 enum bool isPointer(T) = is(T == U*, U); 54 55 /** 56 * Detect whether `T` is a built-in numeric type (integral or floating 57 * point). 58 */ 59 template isNumeric(T) 60 { 61 static if (!__traits(isArithmetic, T)) 62 enum isNumeric = false; 63 else static if (__traits(isFloating, T)) 64 enum isNumeric = is(T : real); // Not __vector, imaginary, or complex. 65 else static if (is(T U == enum)) 66 enum isNumeric = isNumeric!U; 67 else 68 enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar. 69 && !is(immutable T == immutable bool) && !is(T == __vector); 70 } 71 72 73 74 ///Copy pasted from std.traits for not importing too many things 75 template isFunction(X...) 76 if (X.length == 1) 77 { 78 static if (is(typeof(&X[0]) U : U*) && is(U == function) || 79 is(typeof(&X[0]) U == delegate)) 80 { 81 // x is a (nested) function symbol. 82 enum isFunction = true; 83 } 84 else static if (is(X[0] T)) 85 { 86 // x is a type. Take the type of it and examine. 87 enum isFunction = is(T == function); 88 } 89 else 90 enum isFunction = false; 91 } 92 93 template getParams (alias fn) 94 { 95 static if ( is(typeof(fn) params == __parameters) ) 96 alias getParams = params; 97 } 98 alias Parameters = getParams; 99 enum hasModule(string modulePath) = (is(typeof((){mixin("import ", modulePath, ";");}))); 100 enum hasType(string TypeName) = __traits(compiles, {mixin(TypeName, " _;");}); 101 102 template isEnum(alias s) 103 { 104 static if(is(s == enum)) 105 enum bool isEnum = true; 106 else 107 enum bool isEnum = false; 108 } 109 110 111 template getUDAs(alias symbol) 112 { 113 enum getUDAs = __traits(getAttributes, symbol); 114 } 115 116 template hasUDA(alias symbol, alias UDA) 117 { 118 enum helper = () 119 { 120 bool ret = false; 121 foreach(att; __traits(getAttributes, symbol)) 122 { 123 static if(is(typeof(UDA))) 124 { 125 if(att == UDA) ret = true; 126 } 127 else 128 { 129 if(is(typeof(att) == UDA) || is(att == UDA)) ret = true; 130 } 131 } 132 return ret; 133 }(); 134 enum hasUDA = helper; 135 } 136 template isReference(T) 137 { 138 enum isReference = is(T == class) || is(T == interface); 139 } 140 template hasMethod(T, string method, Params...) 141 { 142 enum hasMethod = __traits(hasMember, T, method) && is(getParams!(__traits(getMember, T, method)) == Params); 143 } 144 145 146 147 size_t enumLength(T)() 148 if(is(T == enum)) 149 { 150 return __traits(allMembers, T).length; 151 } 152 153 154 /** 155 * Used on: 156 * - Static Methods 157 * - Class Names 158 * Used in conjunction to HipExportDFunctions. 159 * You may specify a suffix, if you so, `_suffix` is added 160 * ExportD will do nothing to static methods when building release. However, it will still produce 161 * a function for returning a new class. 162 */ 163 struct ExportD{string suffix;} 164 165 166 /** 167 * Will basically generate an export name such as className_funcSymbol 168 * If it has ExportD with a suffix, it will be basically className_funcSymbol(suffix) 169 */ 170 template generateExportName(string className, alias funcSymbol) 171 { 172 //Means that it has a suffix 173 static if(is(typeof(__traits(getAttributes, funcSymbol)[0]) == ExportD)) 174 enum generateExportName = className~"_"~__traits(identifier, funcSymbol)~"_"~__traits(getAttributes, funcSymbol)[0].suffix; 175 else 176 enum generateExportName = className~"_"~__traits(identifier, funcSymbol); 177 } 178 179 /** 180 * Returns a code to be mixed in. 181 * If isRef, it will call with hipSaveRef for not being colled 182 * funcCallCode can be anything as `className.functionName` or even `new Class` 183 */ 184 private string getExportedFuncImpl(bool isRef, string funcCallCode) 185 { 186 assert(__ctfe); 187 string ret; 188 if(isRef) 189 { 190 ret = q{ 191 import hip.util.lifetime; 192 return cast(typeof(return))hipSaveRef(cast(Object) 193 } ~ funcCallCode~"(__traits(parameters)));}"; 194 } 195 else 196 { 197 ret = "return "~funcCallCode~"(__traits(parameters));}"; 198 } 199 return ret; 200 } 201 202 203 ///ClassT, Ctor, string className 204 ///This class MUST have an interface, because it will bug out when calling the function with `need opCmp for class` 205 template generateExportConstructor(ClassT, string className) 206 { 207 enum impl = () 208 { 209 return "export extern(System) I" ~ className ~ " new" ~ className ~ //export extern(System) Class newClass 210 "(getParams!(__traits(getMember, Class, \"__ctor\"))){"~ //(A a, B b...) 211 getExportedFuncImpl 212 ( 213 true, //IsReference 214 "new "~className 215 ); 216 }(); 217 218 enum generateExportConstructor = impl; 219 } 220 221 /** 222 * It will create a `export extern(System)` function, thus, making it a C callable code. 223 * This function comes from a static method, and has special code injection for making the 224 * GC not collect if it is an object 225 */ 226 template generateExportFunc(string className, alias funcSymbol) 227 { 228 import std.traits:ReturnType; 229 enum impl = () 230 { 231 assert(__ctfe); 232 233 alias RetType = ReturnType!funcSymbol; 234 string ret = "export extern(System) "~RetType.stringof~" "~generateExportName!(className, funcSymbol); 235 ret~= "(getParams!(sym)){"; 236 237 ret~= getExportedFuncImpl 238 ( 239 isReference!(RetType), 240 className~"."~__traits(identifier, funcSymbol) 241 ); 242 243 return ret; 244 }(); 245 246 enum generateExportFunc = impl; 247 } 248 249 250 struct Version 251 { 252 template opDispatch(string s) 253 { 254 mixin(`version(`~s~`)enum opDispatch=true;else enum opDispatch=false;`); 255 } 256 } 257 258 259 260 /** 261 * Intermediary step for getting an alias to the Class type 262 * The difference with HipExportDFunctionsImpl is that it does not generate 263 * Static method output when not in script version. 264 */ 265 mixin template HipExportDFunctionsImpl(string className, Class) 266 { 267 //If the class has ExportD, it will export a function called new(ClassName) 268 //It can't contain more than one constructor. 269 static if(hasUDA!(Class, ExportD)) 270 { 271 static assert( 272 __traits(getOverloads, Class, "__ctor").length == 1, 273 "Can't export class with more than one constructor ("~className~")" 274 ); 275 mixin( 276 generateExportConstructor!(Class, className) 277 ); 278 pragma(msg, "Exported Class "~className); 279 } 280 version(Load_DScript) 281 { 282 //Get all static methods that has ExportD 283 static foreach(sym; getSymbolsByUDA!(Class, ExportD)) 284 { 285 static if(!is(sym == Class)) 286 { 287 //Assert that the symbol to generate does not exists yet 288 static assert(__traits(compiles, mixin(generateExportName!(className, sym))), 289 "ExportD '" ~ generateExportName!(className, sym) ~ 290 "' is not unique, use ExportD(\"SomeName\") for overloading with a suffix"); 291 292 pragma(msg, "Exported "~(generateExportName!(className, sym))); 293 //Func signature 294 //Check if it is a non value type 295 mixin(generateExportFunc!(className, sym)); 296 } 297 } 298 } 299 } 300 301 /** 302 * Iterates through a module and generates `export` function declaration for each 303 * @ExportD function found on it. 304 * If the class itself is @ExportD, it will create a method new(ClassName) to be exported too 305 * * The difference with HipExportDFunctions is that it does not generate 306 * * Static method output when not in script version. 307 */ 308 mixin template HipExportDFunctions(alias mod) 309 { 310 import std.traits:getSymbolsByUDA; 311 pragma(msg, "Exporting ", mod.stringof); 312 static foreach(mem; __traits(allMembers, mod)) 313 { 314 //Currently only supported on classes and structs 315 static if( (is(__traits(getMember, mod, mem) == class) || is(__traits(getMember, mod, mem) == struct) )) 316 { 317 mixin HipExportDFunctionsImpl!(mem, __traits(getMember, mod, mem)); 318 } 319 } 320 } 321 322 323 string attributes(alias member)() 324 { 325 assert(!__ctfe); 326 327 string ret; 328 foreach(attr; __traits(getFunctionAttributes, member)) 329 ret~= attr ~ " "; 330 return ret; 331 } 332 333 334 335 336 template hasOverload(T,string member, OverloadType) 337 { 338 bool impl() 339 { 340 if(!__ctfe) 341 return false; 342 bool ret = false; 343 static foreach(ov; __traits(getVirtualMethods, T, member)) 344 static if(is(typeof(ov) == OverloadType)) 345 ret = true; 346 return ret; 347 } 348 349 enum hasOverload = impl; 350 } 351 352 353 bool isMethodImplemented(T, string member, FuncType)() 354 { 355 if(!__ctfe) 356 return false; 357 bool ret; 358 static foreach(overload; __traits(getVirtualMethods, T, member)) 359 if(is(typeof(overload) == FuncType) && !__traits(isAbstractFunction, overload)) 360 ret = true; 361 return ret; 362 } 363 364 365 /** 366 * Private to forward interface 367 */ 368 string ForwardFunc(alias func, string funcName, string member)() 369 { 370 if(!__ctfe) return null; 371 372 return attributes!func~ " ReturnType!(ov) " ~ funcName ~ "(getParams!(ov))"~ 373 "{ return " ~ member ~ "." ~funcName ~ "(__traits(parameters));}"; 374 } 375 376 mixin template ForwardInterface2(string member, I) if(is(I == interface)) 377 { 378 import hip.util.reflection:isMethodImplemented, ForwardFunc; 379 380 static assert(is(typeof(mixin(member)) : I), 381 "For forwarding the interface, the member "~member~" should be or implement "~I.stringof 382 ); 383 384 static foreach(m; __traits(allMembers, I)) 385 static foreach(ov; __traits(getVirtualMethods, I, m)) 386 { 387 //Check for overloads here 388 static if(!isMethodImplemented!(typeof(this), m, typeof(ov))) 389 mixin(ForwardFunc!(ov, m, member)); 390 } 391 } 392 393 394 395 void GenerateGetterSettersInterfaceImpl(interface_)() 396 { 397 if(!__ctfe) return; 398 399 import hip.util.array; 400 foreach(mem; __traits(allMembers, interface_)) 401 { 402 alias member = __traits(getMember, interface_, mem); 403 if(__traits(isFinalFunction, member)) 404 continue; 405 auto attributes = __traits(getFunctionAttributes, member); 406 if(attributes.contains("ref")) 407 continue; 408 } 409 410 } 411 412 /** 413 * Generates getter and setter for given interface. 414 * - Final methods are excluded. 415 * - `void` return types are excluded 416 * - `const` methods will only generate the const getter 417 */ 418 mixin template GenerateGettersSettersInterface(interface_) if(is(interface_ == interface)) 419 { 420 static foreach(mem; __traits(allMembers, interface_)) 421 { 422 423 } 424 } 425 426 /** 427 * This mixin is able to generate runtime accessors. That means that by having a string, it is 428 *possible to modify 429 */ 430 mixin template GenerateRuntimeAccessors() 431 { 432 T* getProperty(T)(string prop) 433 { 434 alias T_this = typeof(this); 435 436 switch(prop) 437 { 438 static foreach(member; __traits(allMembers, T_this)) 439 { 440 static if(is(typeof(__traits(getMember, T_this, member)) == T)) 441 { 442 case member: 443 return &__traits(getMember, T_this, member); 444 } 445 } 446 default: 447 return null; 448 } 449 } 450 451 void setProperty(T)(string propName, T value) 452 { 453 T* prop = getProperty!T(propName); 454 if(prop !is null) 455 *prop = value; 456 } 457 }